﻿/*
 * Copyright (C) 2021. Huawei Device Co., Ltd. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the MIT License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * MIT License for more details.
 */

#include "../stdafx.h"
#include "UIHotKey.h"
namespace DuiLib {
CHotKeyWnd::CHotKeyWnd(void) : m_pOwner(nullptr), m_hBkBrush(nullptr), m_bInit(false) {}

CHotKeyWnd::~CHotKeyWnd() {}

void CHotKeyWnd::Init(CHotKeyUI *pOwner)
{
    m_pOwner = pOwner;
    do {
        if (m_pOwner == nullptr) {
            break;
        }
        RECT rcPos = CalPos();
        UINT uStyle = WS_CHILD | ES_AUTOHSCROLL;
        HWND hWnd = Create(m_pOwner->GetManager()->GetPaintWindow(), nullptr, uStyle, 0, rcPos);
        if (!IsWindow(hWnd)) {
            break;
        }
        SetWindowFont(m_hWnd, m_pOwner->GetManager()->GetFontInfo(m_pOwner->GetFont())->hFont, TRUE);
        SetHotKey(m_pOwner->m_wVirtualKeyCode, m_pOwner->m_wModifiers);
        m_pOwner->m_sText = GetHotKeyName();
        ::EnableWindow(m_hWnd, m_pOwner->IsEnabled() == true);
        ::ShowWindow(m_hWnd, SW_SHOWNOACTIVATE);
        ::SetFocus(m_hWnd);
        m_bInit = true;
    } while (0);
}


RECT CHotKeyWnd::CalPos()
{
    if (m_pOwner == nullptr) {
        return RECT { 0 };
    }
    CDuiRect rcPos = m_pOwner->GetPos();
    RECT rcInset = m_pOwner->GetTextPadding();
    rcPos.left += rcInset.left;
    rcPos.top += rcInset.top;
    rcPos.right -= rcInset.right;
    rcPos.bottom -= rcInset.bottom;
    LONG lHeight = m_pOwner->GetManager()->GetFontInfo(m_pOwner->GetFont())->tm.tmHeight;
    const int mutiples = 2;
    if (lHeight < rcPos.GetHeight()) {
        rcPos.top += (rcPos.GetHeight() - lHeight) / mutiples;
        rcPos.bottom = rcPos.top + lHeight;
    }
    return rcPos;
}


LPCTSTR CHotKeyWnd::GetWindowClassName() const
{
    return _T("HotKeyClass");
}


void CHotKeyWnd::OnFinalMessage(HWND)
{
    // Clear reference and die
    if (m_hBkBrush != nullptr) {
        ::DeleteObject(m_hBkBrush);
    }
    if (m_pOwner != nullptr && m_pOwner->m_pWindow != nullptr) {
        delete m_pOwner->m_pWindow;
        m_pOwner->m_pWindow = nullptr;
    }
    delete this;
}

LRESULT CHotKeyWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lRes = 0;
    BOOL bHandled = TRUE;
    if (uMsg == WM_KILLFOCUS) {
        lRes = OnKillFocus(uMsg, wParam, lParam, bHandled);
    } else if (uMsg == OCM_COMMAND) {
        if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE) {
            lRes = OnEditChanged(uMsg, wParam, lParam, bHandled);
        } else if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE) {
            RECT rcClient;
            ::GetClientRect(m_hWnd, &rcClient);
            ::InvalidateRect(m_hWnd, &rcClient, FALSE);
        }
    } else if (uMsg == WM_KEYDOWN && TCHAR(wParam) == VK_RETURN) {
        m_pOwner->GetManager()->SendNotify(m_pOwner, _T("return"));
    } else if ((uMsg == WM_NCACTIVATE) || (uMsg == WM_NCACTIVATE) || (uMsg == WM_NCCALCSIZE)) {
        return 0;
    } else if (uMsg == WM_PAINT) {
        if (m_pOwner == nullptr) {
            return 0;
        }
        PAINTSTRUCT ps = { 0 };
        HDC hDC = ::BeginPaint(m_hWnd, &ps);
        DWORD dwTextColor = m_pOwner->GetTextColor();
        DWORD dwBkColor = m_pOwner->GetNativeBkColor();
        CDuiString strText = GetHotKeyName();
        ::RECT rect;
        ::GetClientRect(m_hWnd, &rect);
        ::SetBkMode(hDC, TRANSPARENT);
        ::SetTextColor(hDC, RGB(GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor)));
        HBRUSH hBrush = CreateSolidBrush(RGB(GetBValue(dwBkColor), GetGValue(dwBkColor), GetRValue(dwBkColor)));
        ::FillRect(hDC, &rect, hBrush);
        ::DeleteObject(hBrush);
        HFONT hOldFont = (HFONT)SelectObject(hDC, GetWindowFont(m_hWnd));
        ::SIZE size = { 0 };
        ::GetTextExtentPoint32(hDC, strText.GetData(), strText.GetLength(), &size);
        ::DrawText(hDC, strText.GetData(), -1, &rect, DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX);
        ::SelectObject(hDC, hOldFont);
        ::SetCaretPos(size.cx, 0);
        ::EndPaint(m_hWnd, &ps);
        bHandled = TRUE;
    } else {
        bHandled = FALSE;
    }
    if (!bHandled) {
        return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
    }
    return lRes;
}


LPCTSTR CHotKeyWnd::GetSuperClassName() const
{
    return HOTKEY_CLASS;
}


LRESULT CHotKeyWnd::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    LRESULT lRes = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
    ::SendMessage(m_hWnd, WM_CLOSE, 0, 0);
    return lRes;
}


LRESULT CHotKeyWnd::OnEditChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
    if (!m_bInit) {
        return 0;
    }
    if (m_pOwner == nullptr) {
        return 0;
    }
    GetHotKey(m_pOwner->m_wVirtualKeyCode, m_pOwner->m_wModifiers);
    if (m_pOwner->m_wVirtualKeyCode == 0) {
        m_pOwner->m_sText = _T("无");
        m_pOwner->m_wModifiers = 0;
    } else {
        m_pOwner->m_sText = GetHotKeyName();
    }
    m_pOwner->GetManager()->SendNotify(m_pOwner, _T("textchanged"));
    return 0;
}


void CHotKeyWnd::SetHotKey(WORD wVirtualKeyCode, WORD wModifiers)
{
    ASSERT(::IsWindow(m_hWnd));
    ::SendMessage(m_hWnd, HKM_SETHOTKEY, MAKEWORD(wVirtualKeyCode, wModifiers), 0L);
}

DWORD CHotKeyWnd::GetHotKey() const
{
    ASSERT(::IsWindow(m_hWnd));
    return static_cast<DWORD>(::SendMessage(m_hWnd, HKM_GETHOTKEY, 0, 0L));
}

void CHotKeyWnd::GetHotKey(WORD &wVirtualKeyCode, WORD &wModifiers) const
{
    // ASSERT(::IsWindow(m_hWnd));
    // LRESULT dw = ::SendMessage(m_hWnd, HKM_GETHOTKEY, 0, 0L);
    DWORD dw = GetHotKey();
    wVirtualKeyCode = LOBYTE(LOWORD(dw));
    wModifiers = HIBYTE(LOWORD(dw));
}

void CHotKeyWnd::SetRules(WORD wInvalidComb, WORD wModifiers)
{
    ASSERT(::IsWindow(m_hWnd));
    ::SendMessage(m_hWnd, HKM_SETRULES, wInvalidComb, MAKELPARAM(wModifiers, 0));
}


CDuiString CHotKeyWnd::GetKeyName(UINT vk, BOOL fExtended)
{
    UINT nScanCode = ::MapVirtualKeyEx(vk, 0, ::GetKeyboardLayout(0));
    switch (vk) {
            // Keys which are "extended" (except for Return which is Numeric Enter as extended)
        case VK_INSERT:
        case VK_DELETE:
        case VK_HOME:
        case VK_END:
        case VK_NEXT:  // Page down
        case VK_PRIOR: // Page up
        case VK_LEFT:
        case VK_RIGHT:
        case VK_UP:
        case VK_DOWN:
            nScanCode |= 0x100; // Add extended bit
    }
    if (fExtended)
        nScanCode |= 0x01000000L;
    const int offset = 16;
    TCHAR szStr[ MAX_PATH ] = {0};
    ::GetKeyNameText(nScanCode << offset, szStr, MAX_PATH);

    return CDuiString(szStr);
}


CDuiString CHotKeyWnd::GetHotKeyName()
{
    ASSERT(::IsWindow(m_hWnd));

    CDuiString strKeyName;
    WORD wCode = 0;
    WORD wModifiers = 0;
    const TCHAR szPlus[] = _T(" + ");

    GetHotKey(wCode, wModifiers);
    if (wCode != 0 || wModifiers != 0) {
        if (wModifiers & HOTKEYF_CONTROL) {
            strKeyName += GetKeyName(VK_CONTROL, FALSE);
            strKeyName += szPlus;
        }


        if (wModifiers & HOTKEYF_SHIFT) {
            strKeyName += GetKeyName(VK_SHIFT, FALSE);
            strKeyName += szPlus;
        }


        if (wModifiers & HOTKEYF_ALT) {
            strKeyName += GetKeyName(VK_MENU, FALSE);
            strKeyName += szPlus;
        }


        strKeyName += GetKeyName(wCode, wModifiers & HOTKEYF_EXT);
    }

    return strKeyName;
}


IMPLEMENT_DUICONTROL(CHotKeyUI)

CHotKeyUI::CHotKeyUI()
    : m_pWindow(nullptr), m_wVirtualKeyCode(0), m_wModifiers(0), m_uButtonState(0), m_dwHotKeybkColor(0xFFFFFFFF)
{
    const int widthValue = 4;
    const int highValue = 3;
    SetTextPadding(CDuiRect(widthValue, highValue, widthValue, highValue));
    SetBkColor(0xFFFFFFFF);
}

CHotKeyUI::~CHotKeyUI() {}

LPCTSTR CHotKeyUI::GetClass() const
{
    return _T("HotKeyUI");
}

LPVOID CHotKeyUI::GetInterface(LPCTSTR pstrName)
{
    if (_tcscmp(pstrName, _T("HotKey")) == 0) {
        return static_cast<CHotKeyUI *>(this);
    }
    return CLabelUI::GetInterface(pstrName);
}

UINT CHotKeyUI::GetControlFlags() const
{
    if (!IsEnabled()) {
        return CControlUI::GetControlFlags();
    }

    return UIFLAG_SETCURSOR | UIFLAG_TABSTOP;
}

void CHotKeyUI::DoEvent(TEventUI &event)
{
    if (!IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND) {
        if (m_pParent != nullptr) {
            m_pParent->DoEvent(event);
        } else {
            CLabelUI::DoEvent(event);
        }
        return;
    }

    if (event.Type == UIEVENT_SETCURSOR && IsEnabled()) {
        ::SetCursor(::LoadCursor(nullptr, MAKEINTRESOURCE(IDC_IBEAM)));
        return;
    }
    if (event.Type == UIEVENT_WINDOWSIZE) {
        if (m_pWindow != nullptr && m_pManager != nullptr) {
            m_pManager->SetFocusNeeded(this);
        }
    }
    if (event.Type == UIEVENT_SCROLLWHEEL) {
        if (m_pWindow != nullptr) {
            return;
        }
    }
    if (event.Type == UIEVENT_SETFOCUS && IsEnabled()) {
        m_pWindow = new CHotKeyWnd();
        if (m_pWindow == nullptr) {
            return;
        }
        m_pWindow->Init(this);
        Invalidate();
    }
    if (event.Type == UIEVENT_KILLFOCUS && IsEnabled()) {
        Invalidate();
    }
    if (event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK || event.Type == UIEVENT_RBUTTONDOWN) {
        if (IsEnabled()) {
            GetManager()->ReleaseCapture();
            if (IsFocused() && m_pWindow == nullptr) {
                m_pWindow = new CHotKeyWnd();
                if (m_pWindow == nullptr) {
                    return;
                }
                m_pWindow->Init(this);
            }
        }
        return;
    }
    if (event.Type == UIEVENT_MOUSEMOVE) {
        return;
    }
    if (event.Type == UIEVENT_BUTTONUP) {
        return;
    }
    if (event.Type == UIEVENT_CONTEXTMENU) {
        return;
    }
    if (event.Type == UIEVENT_MOUSEENTER) {
        if (IsEnabled()) {
            m_uButtonState |= UISTATE_HOT;
            Invalidate();
        }
        return;
    }
    if (event.Type == UIEVENT_MOUSELEAVE) {
        if (IsEnabled()) {
            m_uButtonState &= ~UISTATE_HOT;
            Invalidate();
        }
        return;
    }
    CLabelUI::DoEvent(event);
}

void CHotKeyUI::SetEnabled(bool bEnable)
{
    CControlUI::SetEnabled(bEnable);
    if (!IsEnabled()) {
        m_uButtonState = 0;
    }
}

void CHotKeyUI::SetText(LPCTSTR pstrText)
{
#ifdef SWITCH_LANGUAGE_TEST
    CDuiString text(pstrText);
    vector<DuiLib::CDuiString> valueList = text.Split(L"@UI@");
    const size_t containSize = 2;
    if (valueList.size() == containSize) {
        m_sText = valueList[0];
        m_sTextAll = valueList[1];
    } else {
#endif
        m_sText = pstrText;
#ifdef SWITCH_LANGUAGE_TEST
    }
#endif
    if (m_pWindow != nullptr)
        Edit_SetText(*m_pWindow, m_sText);
    Invalidate();
}

LPCTSTR CHotKeyUI::GetNormalImage()
{
    return m_sNormalImage;
}

void CHotKeyUI::SetNormalImage(LPCTSTR pStrImage)
{
    m_sNormalImage = pStrImage;
    Invalidate();
}

LPCTSTR CHotKeyUI::GetHotImage()
{
    return m_sHotImage;
}

void CHotKeyUI::SetHotImage(LPCTSTR pStrImage)
{
    m_sHotImage = pStrImage;
    Invalidate();
}

LPCTSTR CHotKeyUI::GetFocusedImage()
{
    return m_sFocusedImage;
}

void CHotKeyUI::SetFocusedImage(LPCTSTR pStrImage)
{
    m_sFocusedImage = pStrImage;
    Invalidate();
}

LPCTSTR CHotKeyUI::GetDisabledImage()
{
    return m_sDisabledImage;
}

void CHotKeyUI::SetDisabledImage(LPCTSTR pStrImage)
{
    m_sDisabledImage = pStrImage;
    Invalidate();
}

void CHotKeyUI::SetNativeBkColor(DWORD dwBkColor)
{
    m_dwHotKeybkColor = dwBkColor;
}

DWORD CHotKeyUI::GetNativeBkColor() const
{
    return m_dwHotKeybkColor;
}

void CHotKeyUI::SetPos(RECT rc)
{
    CControlUI::SetPos(rc);
    if (m_pWindow != nullptr) {
        RECT rcPos = m_pWindow->CalPos();
        ::SetWindowPos(m_pWindow->GetHWND(), nullptr, rcPos.left, rcPos.top, rcPos.right - rcPos.left,
            rcPos.bottom - rcPos.top, SWP_NOZORDER | SWP_NOACTIVATE);
    }
}

void CHotKeyUI::SetVisible(bool bVisible)
{
    CControlUI::SetVisible(bVisible);
    if (m_pManager != nullptr && !IsVisible()) {
        m_pManager->SetFocus(nullptr);
    }
}

void CHotKeyUI::SetInternVisible(bool bVisible)
{
    if (m_pManager != nullptr && !IsVisible()) {
        m_pManager->SetFocus(nullptr);
    }
}

SIZE CHotKeyUI::EstimateSize(SIZE szAvailable)
{
    const int cyStep = 6;
    if (m_cxyFixed.cy == 0) {
        return CDuiSize(m_cxyFixed.cx, m_pManager->GetFontInfo(GetFont())->tm.tmHeight + cyStep);
    }
    return CControlUI::EstimateSize(szAvailable);
}

void CHotKeyUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
{
    const int radix = 16;
    if (pstrValue == nullptr) {
        return;
    }
    if (_tcscmp(pstrName, _T("normalimage")) == 0) {
        SetNormalImage(pstrValue);
    } else if (_tcscmp(pstrName, _T("hotimage")) == 0) {
        SetHotImage(pstrValue);
    } else if (_tcscmp(pstrName, _T("focusedimage")) == 0) {
        SetFocusedImage(pstrValue);
    } else if (_tcscmp(pstrName, _T("disabledimage")) == 0) {
        SetDisabledImage(pstrValue);
    } else if (_tcscmp(pstrName, _T("nativebkcolor")) == 0) {
        if (*pstrValue == _T('#')) {
            pstrValue = ::CharNext(pstrValue);
        }
        LPTSTR pstr = nullptr;
        DWORD clrColor = _tcstoul(pstrValue, &pstr, radix);
        SetNativeBkColor(clrColor);
    } else {
        CLabelUI::SetAttribute(pstrName, pstrValue);
    }
}

void CHotKeyUI::PaintStatusImage(HDC hDC)
{
    if (IsFocused()) {
        m_uButtonState |= UISTATE_FOCUSED;
    } else {
        m_uButtonState &= ~UISTATE_FOCUSED;
    }
    if (!IsEnabled()) {
        m_uButtonState |= UISTATE_DISABLED;
    } else {
        m_uButtonState &= ~UISTATE_DISABLED;
    }

    if ((m_uButtonState & UISTATE_DISABLED) != 0) {
        if (!m_sDisabledImage.IsEmpty()) {
            if (!DrawImage(hDC, (LPCTSTR)m_sDisabledImage)) {
            } else {
                return;
            }
        }
    } else if ((m_uButtonState & UISTATE_FOCUSED) != 0) {
        if (!m_sFocusedImage.IsEmpty()) {
            if (!DrawImage(hDC, (LPCTSTR)m_sFocusedImage)) {
            } else {
                return;
            }
        }
    } else if ((m_uButtonState & UISTATE_HOT) != 0) {
        if (!m_sHotImage.IsEmpty()) {
            if (!DrawImage(hDC, (LPCTSTR)m_sHotImage)) {
            } else {
                return;
            }
        }
    }

    if (!m_sNormalImage.IsEmpty()) {
        if (!DrawImage(hDC, (LPCTSTR)m_sNormalImage)) {
        } else {
            return;
        }
    }
}

void CHotKeyUI::PaintText(HDC hDC)
{
    if (m_dwTextColor == 0 && m_pManager != nullptr) {
        m_dwTextColor = m_pManager->GetDefaultFontColor();
    }
    if (m_dwDisabledTextColor == 0 && m_pManager != nullptr) {
        m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor();
    }
    if (m_sText.IsEmpty()) {
        return;
    }
    CDuiString sText = m_sText;
    RECT rc = m_rcItem;
    rc.left += m_rcTextPadding.left;
    rc.right -= m_rcTextPadding.right;
    rc.top += m_rcTextPadding.top;
    rc.bottom -= m_rcTextPadding.bottom;
    if (IsEnabled()) {
        CRenderEngine::DrawText(hDC, m_pManager, rc, sText, m_dwTextColor, m_iFont, DT_SINGLELINE | m_uTextStyle);
    } else {
        CRenderEngine::DrawText(hDC, m_pManager, rc, sText, m_dwDisabledTextColor, m_iFont,
            DT_SINGLELINE | m_uTextStyle);
    }
}

DWORD CHotKeyUI::GetHotKey() const
{
    return (MAKEWORD(m_wVirtualKeyCode, m_wModifiers));
}

void CHotKeyUI::GetHotKey(WORD &wVirtualKeyCode, WORD &wModifiers) const
{
    wVirtualKeyCode = m_wVirtualKeyCode;
    wModifiers = m_wModifiers;
}

void CHotKeyUI::SetHotKey(WORD wVirtualKeyCode, WORD wModifiers)
{
    m_wVirtualKeyCode = wVirtualKeyCode;
    m_wModifiers = wModifiers;
    if (m_pWindow != nullptr) {
        return;
    }
    m_pWindow = new CHotKeyWnd();
    if (m_pWindow == nullptr) {
        return;
    }
    m_pWindow->Init(this);
    Invalidate();
}
} // Duilib